package datensaetze;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.Map;
import java.util.NoSuchElementException;

import exceptions.NoPreferredNameException;
import exceptions.NoSuchItemException;
import utils.MyStringUtils;

public class DatensatzFactory {

	private static boolean isNewTag = false;

	private static String oldTag = null; // In der Regel die Kategoriennummer

	private static String tag = null;

	private static String oldInfo = "";

	private static String s_info = "";

	private static boolean isSatzEnde = false;

	private static String satzAnfang = "SET: ";

	private static String commandPrefix = "###com:"; // Neuer Datensatz oder
														// Kommando

	public static void setCommandPrefix(String satzEnde) {
		DatensatzFactory.commandPrefix = satzEnde;
	}

	private static String st_rohdaten;

	public static void setSatzAnfang(String satzAnfang) {
		if (satzAnfang != null) {
			DatensatzFactory.satzAnfang = satzAnfang;
			MyStringUtils.setTeilstringVorIDN(satzAnfang);
		}
	}

	/**
	 * Liefert ein Kommando oder null.
	 * 
	 * @param br
	 * @return
	 * @throws IOException
	 */
	public static String getNextCommand(BufferedReader br)
			throws IOException,
			NoSuchItemException {
		if (br == null)
			throw new IllegalArgumentException(
					"Buffered reader oder actions sind null");
		br.mark(1024); // um br zurücksetzen zu können.
		String line = skipBlankLines(br);
		if (line == null) // Streamende erreicht
			return null;
		if (line.startsWith(commandPrefix))
			return line;
		else {
			// dann gibt es kein Kommando, daher zurücksetzen:
			br.reset();
			throw new NoSuchItemException("Kein Kommando, sondern Datensatz");
		}
	}

	/**
	 * Erzeugt einen Datensatz aus einer Stringrepräsentation. Die idn wird als
	 * unbekannt vorausgesetzt.
	 * 
	 * @param actions	
	 * @param daten
	 * @param idn
	 * @return
	 * @throws NoPreferredNameException
	 * @throws NoSuchElementException
	 */
	public static Datensatz createDatensatzAusVollanzeige(
			Map<String, Analysator> actions,
			String daten,
			String idn) throws NoPreferredNameException {
		if (daten == null)
			throw new IllegalArgumentException("Daten sind null");
		Datensatz dsv = null;
		try {
			dsv = createDatensatzAusVollanzeigeOhneIDN(actions,
					new BufferedReader(new StringReader(daten)), idn);
		} catch (IOException e) {
			// gar nichts, da String
		}
		dsv.setIdn(idn);
		return dsv;
	}

	/**
	 * Gibt einen gültigen Datensatz oder null.
	 * 
	 * @param actions
	 * @param br
	 * @return	Datensatz oder null am Ende von br.
	 * @throws IOException
	 * @throws NoPreferredNameException
	 * @throws NoSuchElementException
	 */
	public static Datensatz createDatensatzAusVollanzeige(
			Map<String, Analysator> actions,
			BufferedReader br)
			throws IOException,
			NoSuchItemException,
			NoPreferredNameException {
		if (br == null)
			throw new IllegalArgumentException("Buffered reader ist null");

		String line = skipBlankLines(br);
		if (line == null)
			return null;
		br.mark(1024); // um br zurücksetzen zu können.
		if (line.startsWith(commandPrefix)) {
			// dann ist es ein Kommando, kein Datensatz, daher zurücksetzen:
			br.reset();
			throw new NoSuchItemException("Kein Datensatz, sondern Kommando");
		}
		// jetzt beginnt der Datensatz, daher ein Versuch, die idn zu ermitteln:
		String idn = MyStringUtils.extractIDN(line);

		return createDatensatzAusVollanzeigeOhneIDN(actions, br, idn);
	}

	/**
	 * Geht zur ersten nicht leeren Zeile und gibt diese oder null zurück.
	 * 
	 * @param br
	 *            nicht null
	 * @return erste nicht leere Zeile oder null
	 * @throws IOException
	 */
	private static String skipBlankLines(BufferedReader br) throws IOException {
		if (br == null)
			throw new IllegalArgumentException("Buffered reader ist null");
		// Leere Zeilen zu Beginn überspringen:
		String line;
		do {
			line = br.readLine();
			if (line == null)
				return null;
		} while (line.trim().length() == 0);
		return line;
	}

	/**
	 * Geht nicht davon aus, dass eine IDN in br enthalten ist.
	 * 
	 * Die idn ist eventuell schon anderweitig ermittelt worden, z.B. in der
	 * aufrugenden Funktion 	 * 
	 * createDatensatzAusVollanzeige(Map<String, Analysator>, String, String)
	 * 
	 * @param actions
	 * @param br
	 * @param idn
	 * @return
	 * @throws IOException
	 * @throws NoPreferredNameException
	 * @throws NoSuchElementException
	 */
	public static Datensatz createDatensatzAusVollanzeigeOhneIDN(
			Map<String, Analysator> actions,
			BufferedReader br,
			String idn) throws IOException, NoPreferredNameException {
		if (br == null)
			throw new IllegalArgumentException("Buffered reader ist null");
		// Initialisieren der Klassenvariablen
		isSatzEnde = false;
		isNewTag = false;
		oldTag = tag = oldInfo = s_info = null;
		st_rohdaten = "";

		Datensatz sw = new Datensatz();

		sw.setIdn(idn);
		// br Zeile für Zeile lesen und analysieren. Wenn neuerDatensatz ==
		// true,
		// oder Stream zu Ende, den Rest auswerten,
		// die Factory verlassen und den Eingabestrom zurücksetzen.
		String line;
		do {
			br.mark(1024); // um br zurücksetzen zu können.
			line = skipBlankLines(br);
			if (line == null)
				break;
			buildLineAndTag(line);
			if (isNewTag) // Das sollte der Normalfall sein: jede Zeile ein
							// neuer TagDB
				analyzeTag(sw, oldTag, oldInfo, actions);
			if (isSatzEnde) {
				br.reset();
				break;
			}
		} while (true);
		// so oder so ist das Ende des Satzes erreicht,
		// daher den letzten TagDB noch verarbeiten:
		analyzeTag(sw, tag, s_info, actions);
		sw.setRohdaten(st_rohdaten);
		return sw;

	}

	/**
	 * Zerlegt line in den TagDB am Anfang (beginnt immer mit Zahl) und dem Rest.
	 * Der TagDB wird in tag, der Rest in info gespeichert. Beginnt line nicht mit
	 * einem TagDB, so wird sie an info angehängt.
	 * 
	 * Erkennt, ob Satzende erreicht ist, und setzt dann isSatzEnde = true
	 * 
	 * @param line
	 *            Eine nicht leere Zeile, eventuell mit einem TagDB beginnend.
	 */
	private static void buildLineAndTag(String line) {

		isNewTag = false;

		// Neuer Datensatz erreicht?
		if (line.startsWith(satzAnfang) || line.startsWith(commandPrefix)) {
			isSatzEnde = true;
			return;
		}
		st_rohdaten += line + "\n";

		// Tags beginnen mit einer Zahl und enden mit dem ersten Blank
		char first = line.charAt(0);
		int pos = line.indexOf(' ');
		if (Character.isDigit(first) && pos != -1) { // Neuer Zustand

			oldTag = tag;
			tag = line.substring(0, pos);
			isNewTag = true;
			oldInfo = s_info;
			s_info = line.substring(pos + 1).trim();
			return;

		} else
			// hängt wohl was in die nächste Zeile über, daher anfügen.
			s_info += line;
	}

	private static void analyzeTag(
			Datensatz datensatz,
			String thisTag,
			String info,
			Map<String, Analysator> actions) throws NoPreferredNameException {
		// Da sind wir noch am Angfang:
		if (oldTag == null)
			return;

		if (actions == null)
			return;

		Analysator analysator = actions.get(thisTag);
		if (analysator == null)
			return;

		String magisch = analysator.getMainTag();
		Feld feld = datensatz.getFeld(magisch);
		boolean recentlyCreated = false;
		if (feld == null) {
			feld = analysator.getNeuesFeld();
			feld.setDatensatz(datensatz);
			recentlyCreated = true;
		}

		try {
			boolean success = analysator.analyzeTag(feld, thisTag, info);
			if(success && recentlyCreated) // Feld taugt nix
				datensatz.setFeld(magisch, feld);
		} catch (NoPreferredNameException pnEx) {
			// Kann nicht ignoriert werden
			String s = pnEx.getMessage() + " IDN='" + datensatz.getIdn() + "'";
			throw new NoPreferredNameException(s);
		}
	}

	public static Datensatz createPersonAusExpansion(String zeileOhneTag) {
		Datensatz ds = new Datensatz();
		return null;
	}

	/**
	 * 
	 * Erwartet ein Form- oder Zeitschlagwort 
	 * 		:z Geschichte 1993-2010 
	 * oder ein eventuell mehrgliedriges Schlagwort inclusive IDN 
	 * 		!942559126!|p|Strauß, Botho / |t|Der @Park 
	 * 
	 * Ansonsten wird ein Fehler gemeldet.
	 * 
	 * Am Ende steht, wenn ales gut geht, im Feld idn die IDN und im Feld
	 * felder ein Feld800 mit der Ansetzungskette:
	 * 
	 * 		[('z', Geschichte 1993-2010)] 
	 * 		"942559126",[('p', "Strauß, Botho"), ('t',"Der @Park")]
	 * 	
	 * Im letzteren Fall müßte für die GND noch ein weiterer verknüpfter
	 * Datensatz mit "Strauß, Botho" angelegt werden.
	 * 
	 * @param zeileOhneTag nicht null
	 * @return Gültiger Datensatz mit Feld800 ohne Bemerkung
	 * @throws NoPreferredNameException		Wenn die übergebene Zeile nicht 
	 * 										syntaktisch korrekt ist. 
	 * @throws IllegalArgumentException
	 */
	public static Datensatz createSWAusExpansion(final String zeileOhneTag)
			throws NoPreferredNameException {
		return null;
	}

	/**
	 * 
	 * Keine Überprüfung auf syntaktische Korrektheit. Also auch nicht, 
	 * ob in Unterschlagworten fälschlich ein "|" vorkommt.
	 * 
	 * @param zeileOhneTag nicht null.
	 * @return	Gültigen Datensatz ohne idn, aber mit Bemerkung
	 * @throws NoPreferredNameException
	 */
	public static Datensatz createSWAusSWFeld(String zeileOhneTag)
			throws NoPreferredNameException {

		return null;
	}

}
